home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2010 April
/
PCWorld0410.iso
/
pluginy Firefox
/
2410
/
2410.xpi
/
chrome
/
content
/
foxmarks-rdf.js
< prev
next >
Wrap
Text File
|
2010-01-28
|
30KB
|
809 lines
/*
Copyright 2007-2008 Foxmarks Inc.
foxmarks-rdf.js: implements class BookmarkDatasource, encapsulating
the Firefox RDF datasource.
*/
// To do:
// * icons in 1.0.1: whoops!
// * fix (ahem) exception text
// * figure out how to move helper mapping functions back into prototype
// * Never set a date backward in time.
var Cc = Components.classes;
var Ci = Components.interfaces;
const RDF_ROOT = "NC:BookmarksRoot";
const NC = "http://home.netscape.com/NC-rdf#";
const NS = "http://home.netscape.com/WEB-rdf#";
const RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
function ParseIconString(s) {
var exp = /^data:(.*);base64,(.*)$/;
var match = exp.exec(s);
if (match) {
try {
var data = My_atob(match[2]);
} catch (e) {
return null;
}
return [match[1] /* mime type */, data /* b64-encoded data */];
} else {
return null;
}
}
// Node's representation of dates is seconds since 1/1/1970.
// RDF's representation of dates is microseconds since 1/1/1970.
// Use these utility functions to convert between the two formats
// while minimizing rounding error.
function DateRDFToNode(v) {
return Math.round(v / 1000000);
}
function DateNodeToRDF(v) {
return v * 1000000;
}
const MAP_RDF_TYPE = {
"http://home.netscape.com/NC-rdf#Bookmark" : "bookmark",
"http://home.netscape.com/NC-rdf#Folder" : "folder",
"http://home.netscape.com/NC-rdf#BookmarkSeparator" : "separator",
"http://home.netscape.com/NC-rdf#Livemark" : "feed",
"http://home.netscape.com/NC-rdf#MicsumBookmark" : "microsummary"
}
const MAP_NODE_TYPE = {
"bookmark" : null, // special case: bookmarks & folders
"folder" : null, // have their types inferred
"separator" : NC + "BookmarkSeparator",
"feed" : NC + "Livemark",
"microsummary" : NC + "MicsumBookmark"
};
// Node type requires special mapping
function MapRDFType(node, resource, predicate, target) {
if (target instanceof Ci.nsIRDFResource) {
node.ntype = MAP_RDF_TYPE[target.Value];
} else {
throw Error("Whatever! I do what I want!");
}
};
// Returns either:
// 1) array of [pred, targ, targtype (literal vs. resource)]
// 2) null (skip it)
// 3) string (reset incoming attribute to this type and skip assertion)
function MapNodeType(ntype) {
if (ntype in MAP_NODE_TYPE) {
var map = MAP_NODE_TYPE[ntype];
if (map) {
return [RDF + "type", map, true];
} else {
return null;
}
} else {
return "bookmark";
}
}
// Sidebar requires special mapping, as it's stored in RDF as a literal
// but we want to present as boolean in our node structure.
function MapRDFSidebar(node, resource, predicate, target) {
node.sidebar = true;
}
function MapNodeSidebar(sidebar) {
if (sidebar) {
return [NC + "WebPanel", "true", false];
} else {
return [null, null, false];
}
}
function BookmarkDatasource() {
this.rdfs = Cc["@mozilla.org/rdf/rdf-service;1"].getService(
Ci.nsIRDFService);
this.ds = this.rdfs.GetDataSource("rdf:bookmarks").
QueryInterface(Ci.nsIBookmarksService);
this.container = Cc["@mozilla.org/rdf/container;1"].
createInstance(Ci.nsIRDFContainer);
this.rdfcu = Cc["@mozilla.org/rdf/container-utils;1"].
getService(Ci.nsIRDFContainerUtils);
this.lastModifiedPredicate = this.rdfs.
GetResource("http://home.netscape.com/WEB-rdf#LastModifiedDate");
this.addDatePredicate = this.rdfs.
GetResource("http://home.netscape.com/NC-rdf#BookmarkAddDate");
this.typePredicate = this.rdfs.
GetResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
if (!BookmarkDatasource._mapNidToNative) {
this._InitNidNativeMaps();
}
applyCommonBookmarkFunctions(this);
}
// For backwards compatibility, this must be an empty string
BookmarkDatasource.STORAGE_ENGINE = "";
BookmarkDatasource.startupTime = Date.now()
BookmarkDatasource.ProvideNodesDone = function(status) {
var tb = this.GetToolbar();
if (tb) {
this.pn.Caller.Node(NODE_ROOT, true).tnid = tb;
}
this.pn.Complete.apply(this.pn.Caller, [status]);
this.pn.Caller.rdfSource = true;
this.pn = null;
return;
}
BookmarkDatasource.MapRDFToNode = function(resource, pnid) {
var nid = this.MapNative(resource.Value);
var node = new Node(nid);
var predicates = this.ds.ArcLabelsOut(resource);
node.pnid = pnid;
while (predicates.hasMoreElements()) {
var predicate = predicates.getNext();
if (this.rdfcu.IsOrdinalProperty(predicate))
continue;
var target = this.ds.GetTarget(resource, predicate, true);
// sometimes, targets don't exist (icons in particular)
// just skip 'em
if (!target)
continue;
predicate.QueryInterface(Ci.nsIRDFResource);
target.QueryInterface(Ci.nsIRDFNode);
var map = this.RDF_NODE_MAP[predicate.Value];
var value = null;
if (map === null) {
// do nothing
} else if (typeof(map) == "string") {
if (target instanceof Ci.nsIRDFResource) {
value = String(target.Value);
} else if (target instanceof Ci.nsIRDFLiteral) {
value = String(target.Value);
} else if (target instanceof Ci.nsIRDFDate) {
value = parseInt(target.Value / 1000000.0 + 0.5);
} else {
throw "Erggh! unknown target " + predicate.Value +
"/" + target.Value;
}
node[map] = value;
} else if (typeof(map) == "function") {
map(node, resource, predicate, target);
} else {
throw Error(
"Damn, woman! You trying to kill me or something?" +
predicate.Value + " / " + typeof(map) +
" / " + map);
}
}
if (this.IsContainer(resource)) {
node.ntype = "folder";
node.children = [];
this.container.Init(this.ds, resource);
var children = this.container.GetElements();
while (children.hasMoreElements()) {
var child = children.getNext();
if (child instanceof Ci.nsIRDFResource)
node.children.push(this.MapNative(String(child.Value)));
}
}
if (Xmarks.gSettings.disableIconSync) {
delete node["icon"];
}
this.pn.AddNode.apply(this.pn.Caller, [node]);
return 0;
}
// ot is the static object that maintains state for OnTree,
// the tree-walker.
var ot = {}
BookmarkDatasource.Continue = {
notify: function(timer) {
var self = ot.self;
var resources = ot.resources;
var result;
var s = Date.now();
while (resources.length > 0 && Date.now() - s < 100) {
var next = resources.shift();
var resource = next[0].QueryInterface(Ci.nsIRDFResource);
var pnid = next[1];
try {
result = ot.action.apply(ot.Caller, [resource, pnid]);
} catch (e) {
Xmarks.LogWrite("OnTree error " + e.toSource());
result = 3;
}
if (result)
break;
if (self.IsContainer(resource)) {
self.container.Init(self.ds, resource);
var children = self.container.GetElements();
var ix = 0;
var nid = self.MapNative(resource.Value);
while (children.hasMoreElements()) {
if (ot.depthfirst) {
resources.splice(ix, 0,
[children.getNext(), nid]);
ix += 1;
} else {
resources.push(
[children.getNext(), nid]);
}
}
}
}
if (resources.length > 0 && !result) {
ot.timer.initWithCallback(BookmarkDatasource.Continue, 10,
Ci.nsITimer.TYPE_ONE_SHOT);
} else {
ot.complete.apply(ot.Caller, [result]);
}
}
}
BookmarkDatasource.prototype = {
_InitNidNativeMaps: function() {
BookmarkDatasource._mapNidToNative = {};
BookmarkDatasource._mapNativeToNid = {};
this.AddToMap(RDF_ROOT, NODE_ROOT);
},
MapNid: function(nid) {
return BookmarkDatasource._mapNidToNative[nid] || "rdf:#$" + nid;
},
MapNative: function(resourceId) {
return BookmarkDatasource._mapNativeToNid[resourceId] ||
(resourceId.slice(0, 6) == "rdf:#$" ?
resourceId.slice(6) : this.AddToMap(resourceId, resourceId));
},
AddToMap: function(resourceId, nid) {
BookmarkDatasource._mapNidToNative[nid] = resourceId;
BookmarkDatasource._mapNativeToNid[resourceId] = nid;
return nid;
},
BaselineLoaded: function(baseline, callback) {
var self = this;
self._InitNidNativeMaps();
self.OnTree(this, ScanForSeparators, Done);
return;
function ScanForSeparators(resource, pnid) {
if (!IsSeparator(resource)) {
return 0;
}
var parent = baseline.Node(pnid, false, true);
if (!parent) {
return 0;
}
for (var i = 0; i < parent.children.length; ++i) {
var child = baseline.Node(parent.children[i]);
if (child.ntype == "separator" &&
!BookmarkDatasource._mapNidToNative[child.nid]) {
self.AddToMap(String(resource.Value), child.nid);
break;
}
}
return 0;
}
function Done(status) {
callback(status);
}
function IsSeparator(resource) {
return (self._GetType(resource) ==
"http://home.netscape.com/NC-rdf#BookmarkSeparator");
}
},
MAP_NODE_RDF: {
"nid" : null,
"pnid" : null,
"children" : null,
"tnid" : null,
"ntype" : MapNodeType,
"description" : [NC + "Description", Ci.nsIRDFLiteral],
"name" : [NC + "Name", Ci.nsIRDFLiteral],
"created" : [NC + "BookmarkAddDate", Ci.nsIRDFDate],
"url" : [NC + "URL", Ci.nsIRDFLiteral],
"feedurl" : [NC + "FeedURL", Ci.nsIRDFLiteral],
"sidebar" : MapNodeSidebar,
"shortcuturl" : [NC + "ShortcutURL", Ci.nsIRDFLiteral],
"generateduri" : [NC + "MicsumGenURI", Ci.nsIRDFLiteral],
"generatedtitle": null,
"contenttype" : [NC + "ContentType", Ci.nsIRDFLiteral],
"msexpires" : null,
"icon" : [NC + "Icon", Ci.nsIRDFLiteral],
"formdata" : [NC + "PostData", Ci.nsIRDFLiteral],
"modified" : null,
"visited" : [NS + "LastVisitDate", Ci.nsIRDFDate]
},
IMMUTABLE_RDF: {
"http://home.netscape.com/WEB-rdf#LastModifiedDate" : true,
"http://home.netscape.com/NC-rdf#BookmarksToolbarFolder": true,
"http://home.netscape.com/NC-rdf#ID" : true,
"http://developer.mozilla.org/rdf/vocabulary/forward-proxy#forward-proxy"
: true,
"http://home.netscape.com/NC-rdf#LivemarkExpiration" : true,
"http://home.netscape.com/WEB-rdf#LastCharset" : true,
"http://home.netscape.com/NC-rdf#Icon" : true,
"http://home.netscape.com/NC-rdf#GeneratedTitle" : true,
"http://home.netscape.com/NC-rdf#MicsumExpiration" : true
},
AcceptNodes: function(ns, callback) {
var self = this;
var optimizeOK = ns._cloneSource && ns._cloneSource.rdfSource;
self.StartWrite();
ns.OnTree(FlushNode, Done);
return;
function FlushNode(nid, pnid) {
if (optimizeOK && !ns._node[nid]) {
// We can skip writing this because we know that the
// nodeset in question is cloned from a nodeset that
// originated with the local datastore, and the node
// in question exists only in the clone source; it's
// thus unchanged.
return 0;
}
self.WriteNode(ns, ns.Node(nid));
return 0;
}
function Done(status) {
if (status) {
callback(status);
} else {
// You might think that we should set the toolbar
// before calling EndWrite, but (at least in the case
// of Firefox 2), you'd be wrong: due to a repaint bug,
// the toolbar ignores changes to the designated toolbar
// while it's in the middle of batch updates. One could
// argue that this should be handled in SetToolbar().
self.EndWrite();
self.SetToolbar(ns.Node(NODE_ROOT).tnid);
callback(0);
}
}
},
// Call StartWrite before calling WriteNode
StartWrite: function() {
this.ds.beginUpdateBatch();
},
// Call EndWrite after we're done calling WriteNode
EndWrite: function() {
this.ds.endUpdateBatch();
},
WriteNode: function(ns, node) {
// iterate over node's properties; generate list of
// prospective assertions
if (node == null) {
throw Error("node is null");
}
var assert = [];
var attrs = node.GetSafeAttrs();
for (var attr in attrs) {
if (!attrs.hasOwnProperty(attr)) {
continue;
}
var map = this.MAP_NODE_RDF[attr];
var targ = null;
var pred = null;
var value = attrs[attr];
if (map === null) {
continue; // do nothing
} else if (typeof map == "object") {
// XXX: Optimization: build these in advance
pred = this.rdfs.GetResource(map[0]);
var targType = map[1];
if (targType == Ci.nsIRDFLiteral) {
targ = this.rdfs.GetLiteral(value);
} else if (targType == Ci.nsIRDFDate) {
targ = this.rdfs.GetDateLiteral(DateNodeToRDF(value));
} else {
throw Error("Knuckle up! " + typeof value);
}
} else if (typeof map == "function") {
var a = map(value);
if (!a) {
continue;
} else if (typeof a == 'object') {
pred = this.rdfs.GetResource(a[0]);
targ = a[2] ? this.rdfs.GetResource(a[1]) :
this.rdfs.GetLiteral(a[1]);
} else if (typeof a == "string") {
ns.Node(node.nid, true)[attr] = a;
Xmarks.LogWrite("Warning: Set " + attr + " for node " +
node.nid + " to " + a);
continue;
}
} else {
delete ns.Node(node.nid, true)[attr];
Xmarks.LogWrite("Warning: dropping unrecognized attribute " + attr);
continue;
}
assert.push([pred, targ]);
}
var subj = this.rdfs.GetResource(this.MapNid(node.nid));
if (node.ntype == 'folder') {
// Are we a folder with no children?
if (node["children"] == null) {
node.children = [];
}
for (var i = 0; i < node.children.length; ++i) {
var pred = this.rdfcu.IndexToOrdinalResource(i + 1);
var targ = this.rdfs.GetResource(this.MapNid(node.children[i]));
assert.push([pred, targ]);
}
assert.push([this.rdfs.GetResource(RDF + "nextVal"),
this.rdfs.GetLiteral("" + (1 + node.children.length))]);
// Since we are not asserting the type of bookmark and folder
// nodes, we assert instanceof Seq so that Firefox can infer
// that we're talking about a folder here.
assert.push([this.rdfs.GetResource(RDF + "instanceOf"),
this.rdfs.GetResource(RDF + "Seq")]);
} else if (node.ntype == 'feed') {
// This is some code to keep from deleting children of a
// Livemark
var nextVal = this.ds.GetTarget(subj,
this.rdfs.GetResource(RDF + "nextVal"), true);
if (nextVal) {
assert.push([this.rdfs.GetResource(RDF + "nextVal"), nextVal]);
}
assert.push([this.rdfs.GetResource(RDF + "instanceOf"),
this.rdfs.GetResource(RDF + "Seq")]);
}
// Iterate over existing assertions. For each assertion we find,
// either:
// (1) delete it entirely
// (2) change it to match the new target value
// (3) do nothing, because it is perfect as it is
// In either of the latter two cases, remove the match from
// the prospective list.
var alo = this.ds.ArcLabelsOut(subj);
while (alo.hasMoreElements()) {
var pred = alo.getNext();
var targ = this.ds.GetTarget(subj, pred, true);
var i = FindPredicate(pred);
if (i < 0) {
// No match; delete it.
if (pred instanceof Ci.nsIRDFResource) {
// Don't Unassert if:
// 1) It's immutable, or
// 2) It's a Livemark's ordinal property
if (this.IMMUTABLE_RDF[pred.Value] == null &&
(node.ntype != 'feed' ||
!this.rdfcu.IsOrdinalProperty(pred)) ) {
Xmarks.LogWrite("Unassert("+subj.Value+","+pred.Value+")");
this.ds.Unassert(subj, pred, targ, true);
} else if (pred.Value == NC + "Icon" &&
!Xmarks.gSettings.disableIconSync) {
try {
this.ds.removeBookmarkIcon(node.url);
} catch (e) {
Xmarks.LogWrite("removeBookmarkIcon: " + e.toSource());
}
}
} else {
throw Error("Awwww, man!");
}
} else {
// We matched the predicate. Do we need to change
// the target?
if (!TargetsMatch(targ, assert[i][1])) {
if (this.IMMUTABLE_RDF[pred.Value] == null) {
Xmarks.LogWrite("Change("+subj.Value+","+pred.Value+")");
this.ds.Change(subj, pred, targ, assert[i][1]);
assert.splice(i, 1);
}
} else {
assert.splice(i, 1);
}
}
}
// execute remaining assertions against datastore
for (var i = 0; i < assert.length; ++i) {
var a = assert[i];
a[0].QueryInterface(Ci.nsIRDFResource);
a[1].QueryInterface(Ci.nsIRDFNode);
Xmarks.LogWrite("Asserting " + subj.Value + "," +
a[0].Value + "," + a[1].Value);
if (a[0].Value == NC + "Icon" &&
this.ds instanceof Ci.nsIBookmarksService) {
var iconPieces = ParseIconString(a[1].Value);
if (iconPieces) {
this.ds.updateBookmarkIcon(node["url"],
iconPieces[0], iconPieces[1], iconPieces[1].length);
}
} else {
this.ds.Assert(subj, a[0], a[1], true);
}
}
function FindPredicate(p) {
for (i = 0; i < assert.length; ++i) {
if (assert[i][0] == p)
return i;
}
return -1;
}
function TargetsMatch(t1, t2) {
if (t1 instanceof Ci.nsIRDFDate) {
return (DateRDFToNode(t1.Value) == DateRDFToNode(t2.Value));
} else {
return t1 == t2;
}
}
},
SetToolbar: function(nid) {
if (this.GetToolbar() != nid) {
Xmarks.LogWrite("Setting toolbar nid to " + nid);
this.ds.setBookmarksToolbarFolder(
this.rdfs.GetResource(this.MapNid(nid)));
}
},
GetToolbar: function() {
if (this.ds instanceof Ci.nsIBookmarksService) {
var tb = this.ds.getBookmarksToolbarFolder();
return tb ? this.MapNative(tb.Value) : null;
} else {
throw "Criminy! It's not an nsIBookmarksService";
}
},
// traverses this's bookmarks hierarchy starting with
// startnode, calling action(node) for each node in the tree,
// then calling complete() when traversal is done.
// enforces rules about maximum run times to prevent hanging the UI
// when traversing large trees or when running on slow CPU's.
// action() should return 0 to continue, non-zero status to abort.
// complete() is called with non-zero status if aborted.
// depthfirst determines tree traversal order
OnTree: function(Caller, action, complete, startnode, depthfirst) {
ot = {}
ot.self = this;
ot.Caller = Caller;
ot.action = action;
ot.complete = complete;
ot.startnode = startnode || this.rdfs.GetResource(RDF_ROOT);
ot.depthfirst = depthfirst;
ot.resources = [[ot.startnode, null]];
ot.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
ot.timer.initWithCallback(BookmarkDatasource.Continue,
10, Ci.nsITimer.TYPE_ONE_SHOT);
return;
},
IsContainer: function(resource)
{
if (!this.rdfcu.IsContainer(this.ds, resource))
return false;
// if it's a Livemark, don't consider it a container, as we don't
// process its children
var type = this.ds.GetTarget(resource, this.typePredicate, true);
return (type instanceof Ci.nsIRDFResource &&
type.Value != "http://home.netscape.com/NC-rdf#Livemark");
},
RDF_NODE_MAP: {
"http://www.w3.org/1999/02/22-rdf-syntax-ns#type" : MapRDFType,
"http://home.netscape.com/NC-rdf#Description" : "description",
"http://home.netscape.com/NC-rdf#Name" : "name",
"http://home.netscape.com/NC-rdf#BookmarkAddDate" : "created",
"http://home.netscape.com/NC-rdf#URL" : "url",
"http://home.netscape.com/NC-rdf#FeedURL" : "feedurl",
"http://home.netscape.com/NC-rdf#WebPanel" : MapRDFSidebar,
"http://home.netscape.com/NC-rdf#ShortcutURL" : "shortcuturl",
"http://home.netscape.com/NC-rdf#LivemarkExpiration": null,
"http://home.netscape.com/NC-rdf#Icon" : "icon",
"http://home.netscape.com/NC-rdf#BookmarksToolbarFolder"
: null,
"http://home.netscape.com/WEB-rdf#LastModifiedDate" : "modified",
"http://home.netscape.com/WEB-rdf#LastVisitDate" : "visited",
"http://home.netscape.com/NC-rdf#MicsumGenURI" : "generateduri",
"http://home.netscape.com/NC-rdf#GeneratedTitle" : null,
"http://home.netscape.com/NC-rdf#ContentType" : "contenttype",
"http://home.netscape.com/NC-rdf#MicsumExpiration" : null,
"http://home.netscape.com/NC-rdf#LivemarkLock" : null,
"http://home.netscape.com/NC-rdf#PostData" : "formdata",
"http://www.w3.org/1999/02/22-rdf-syntax-ns#children"
: null,
"http://www.w3.org/1999/02/22-rdf-syntax-ns#parent" : null,
"http://www.w3.org/1999/02/22-rdf-syntax-ns#instanceOf"
: null,
"http://www.w3.org/1999/02/22-rdf-syntax-ns#nextVal"
: null,
"http://home.netscape.com/NC-rdf#ID" : null,
"http://home.netscape.com/WEB-rdf#LastCharset" : null,
"http://developer.mozilla.org/rdf/vocabulary/forward-proxy#forward-proxy"
: null
},
GenerateNid: function() {
var resource = this.rdfs.GetAnonymousResource();
if (resource instanceof Ci.nsIRDFResource) {
return this.MapNative(resource.Value);
} else {
throw Error("Dagnabbit! Couldn't create anonymous resource");
}
},
NormalizeUrl: function(url) {
return url;
},
ProvideNodes: function(Caller, AddNode, Complete) {
this.pn = {}
this.pn.Caller = Caller;
this.pn.AddNode = AddNode;
this.pn.Complete = Complete;
this.OnTree(this, BookmarkDatasource.MapRDFToNode,
BookmarkDatasource.ProvideNodesDone);
return;
},
WatchForChanges: function() {
var watcher = new BookmarkWatcher();
// start observing
this.ds.AddObserver(watcher);
return watcher;
},
_GetType: function(resource) {
var type = this.ds.GetTarget(resource, this.typePredicate, true)
if (type instanceof Ci.nsIRDFResource) {
return type.Value;
} else {
return null;
}
}
};
function BookmarkWatcher(){
this.rdfs = Cc["@mozilla.org/rdf/rdf-service;1"].getService(
Ci.nsIRDFService);
this.ds = this.rdfs.GetDataSource("rdf:bookmarks").
QueryInterface(Ci.nsIBookmarksService);
this.typePredicate = this.rdfs.
GetResource("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
}
BookmarkWatcher.prototype = {
lastModified: null,
NotifyObservers: function(lm) {
// Take in RDF's microseconds since 1970.
// Output Javascript milliseconds since 1970.
lm = Math.round(lm / 1000);
if (!this.lastModified || lm > this.lastModified) {
this.lastModified = lm;
var os = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
os.notifyObservers(null, "foxmarks-datasourcechanged",
lm + ";bookmarks");
}
},
_GetType: function(resource) {
var type = this.ds.GetTarget(resource, this.typePredicate, true)
if (type instanceof Ci.nsIRDFResource) {
return type.Value;
} else {
return null;
}
},
////////////////////////////////////////////////////////////////////////////
//
// nsIRDFObserver
onAssert: function(dataSource, source, property, target) {
},
onUnassert: function(dataSource, source, property, target) {
},
onChange: function(dataSource, source, property, oldTarget, newTarget) {
var self = this;
var funcIsLivemark = function(resource) {
return (self._GetType(resource) ==
"http://home.netscape.com/NC-rdf#Livemark");
};
var funcIsMicsum = function(resource){
return (self._GetType(resource) ==
"http://home.netscape.com/NC-rdf#MicsumBookmark");
}
if (property.QueryInterface(Components.interfaces.nsIRDFResource).Value
== "http://home.netscape.com/WEB-rdf#LastModifiedDate" &&
!funcIsLivemark(source) && !funcIsMicsum(source)) {
self.NotifyObservers(newTarget.
QueryInterface(Ci.nsIRDFDate).Value);
}
},
onBeginUpdateBatch: function(dataSource) {
},
onEndUpdateBatch: function(dataSource) {
// Some kind of changes happened. Alas, the bookmark manager
// has hidden from us exactly what happened. To be safe,
// assume that something has changed and fire a sync as required.
// Oh, and ignore notifications in the first 10 seconds after startup.
if (Date.now() - BookmarkDatasource.startupTime >= 10000) {
if (!Xmarks.gSettings.disableDirtyOnBatch) {
Xmarks.LogWrite("onEndUpdateBatch()");
this.NotifyObservers(Date.now() * 1000);
} else {
Xmarks.LogWrite("ignoring onEndUpdateBatch");
}
}
}
};